#include "BookManager.h"
#include "../Commons/Utils.h"

using namespace Managers;

BookManager::BookManager() {
	Configuration* config = Configuration::getInstance();
	string path = config->getBookFilePath() + "bookFile.dat";
	string booksManager_path(path);
	path = config->getBookFilePath() + "entitiesNotProcessed";
	string entitiesNotProcessedManager_path(path);
	this->booksManager = new VarFileManager(booksManager_path);
	this->entitiesNotProcessedManager = new BlockFileManager(blockSize, entitiesNotProcessedManager_path);

	//check if Entities not processed was never created
	if (entitiesNotProcessedManager->getNumberOfBlocks() == 0) {
		EntityNotProcessed* temp = new EntityNotProcessed(Title);
		entitiesNotProcessedManager->writeNewBlock(temp->buildBuffer());
		temp->setType(Author);
		entitiesNotProcessedManager->writeNewBlock(temp->buildBuffer());
		temp->setType(Publisher);
		entitiesNotProcessedManager->writeNewBlock(temp->buildBuffer());
		temp->setType(Words);
		entitiesNotProcessedManager->writeNewBlock(temp->buildBuffer());
		delete temp;
	}
}

void BookManager::addBook(Book* bookToAdd) {

	unsigned int bookID = booksManager->writeRecord(bookToAdd);

	//TODO: sacar el valor del blockNumber desde el config.file
	addBookOffsetAtEntityNotProcessed(bookID, Title, 0);
	addBookOffsetAtEntityNotProcessed(bookID, Author, 1);
	addBookOffsetAtEntityNotProcessed(bookID, Publisher, 2);
	addBookOffsetAtEntityNotProcessed(bookID, Words, 3);

}

void BookManager::addBookOffsetAtEntityNotProcessed(unsigned int offset, EntityType entityType, unsigned int blockNumber) {

	EntityNotProcessed* entityNotProcessed = new EntityNotProcessed(blockNumber, entityType);
	char* buffer;

	buffer = entitiesNotProcessedManager->readBlock(blockNumber);
	entityNotProcessed->fillEntity(buffer, blockSize);
	delete buffer;

	entityNotProcessed->addOffset(offset);
	buffer = entityNotProcessed->buildBuffer();
	entitiesNotProcessedManager->updateBlock(buffer, blockNumber);
	delete buffer;

	delete entityNotProcessed;
}

vector<Record*>* BookManager::getBooksWithEntityNotProcessed(EntityType type) {

	vector<Record*>* toReturn = new vector<Record*> ;
	unsigned int blockNumber = 0;

	switch (type) {
	case (Title): {

		blockNumber = 0;

		EntityNotProcessed* entityNotProcessed = new EntityNotProcessed(blockNumber, Title);
		char* buffer = entitiesNotProcessedManager->readBlock(blockNumber);
		entityNotProcessed->fillEntity(buffer, blockSize);

		for (unsigned int i = 0; i < entityNotProcessed->getBooksOffsets()->size(); i++) {

			Book* book = new Book();
			booksManager->readRecord(book, entityNotProcessed->getBooksOffsets()->at(i));

			Record* toAdd = Commons::Utils::getRecordIn(book->getTitle(), toReturn);

			if (toAdd == NULL) {
				toAdd = new Record(book->getTitle(), book->getEntityID());
				toReturn->push_back(toAdd);
			} else
				toAdd->insertOffset(book->getEntityID());

			delete book;

		}
		this->setEntitiesProcessedOfType(Title, blockNumber);
		break;
	}
	case (Author): {

		blockNumber = 1;

		EntityNotProcessed* entityNotProcessed = new EntityNotProcessed(blockNumber, Author);
		char* buffer = entitiesNotProcessedManager->readBlock(blockNumber);
		entityNotProcessed->fillEntity(buffer, blockSize);

		for (unsigned int i = 0; i < entityNotProcessed->getBooksOffsets()->size(); i++) {

			Book* book = new Book();
			booksManager->readRecord(book, entityNotProcessed->getBooksOffsets()->at(i));

			Record* toAdd = Commons::Utils::getRecordIn(book->getAuthor(), toReturn);

			if (toAdd == NULL) {
				toAdd = new Record(book->getAuthor(), book->getEntityID());
				toReturn->push_back(toAdd);
			} else
				toAdd->insertOffset(book->getEntityID());

			delete book;

		}
		this->setEntitiesProcessedOfType(Author, blockNumber);
		break;
	}
	case (Publisher): {
		blockNumber = 2;

		EntityNotProcessed* entityNotProcessed = new EntityNotProcessed(blockNumber, Publisher);
		char* buffer = entitiesNotProcessedManager->readBlock(blockNumber);
		entityNotProcessed->fillEntity(buffer, blockSize);

		for (unsigned int i = 0; i < entityNotProcessed->getBooksOffsets()->size(); i++) {

			Book* book = new Book();
			booksManager->readRecord(book, entityNotProcessed->getBooksOffsets()->at(i));

			Record* toAdd = Commons::Utils::getRecordIn(book->getPublisher(), toReturn);

			if (toAdd == NULL) {
				toAdd = new Record(book->getPublisher(), book->getEntityID());
				toReturn->push_back(toAdd);
			} else
				toAdd->insertOffset(book->getEntityID());

			delete book;

		}
		this->setEntitiesProcessedOfType(Publisher, blockNumber);
		break;
	}
	case (Words): {
		blockNumber = 3;

		EntityNotProcessed* entityNotProcessed = new EntityNotProcessed(blockNumber, Words);
		cout<<" aca ";
		char* buffer = entitiesNotProcessedManager->readBlock(blockNumber);
		cout <<" esta ";
		entityNotProcessed->fillEntity(buffer, blockSize);
		cout <<" bien ";
		for (unsigned int i = 0; i < entityNotProcessed->getBooksOffsets()->size(); i++) {

			Book* book = new Book();
			booksManager->readRecord(book, entityNotProcessed->getBooksOffsets()->at(i));

			for (unsigned int j = 0; j < book->getWords()->size(); j++) {
				string word = book->getWords()->at(j);
				Record* toAdd = Commons::Utils::getRecordIn(word, toReturn);

				if (toAdd == NULL) {
					toAdd = new Record(word, book->getEntityID());
					toReturn->push_back(toAdd);
				} else
					toAdd->insertOffset(book->getEntityID());

			}

			delete book;
		}
		this->setEntitiesProcessedOfType(Words, blockNumber);
		delete buffer;
		delete entityNotProcessed;
		break;
	}

	}

	return toReturn;
}
//
void BookManager::setEntitiesProcessedOfType(EntityType type, int blockNumber) {
	EntityNotProcessed* emptyEntity = new EntityNotProcessed(type);
	char* buffer = emptyEntity->buildBuffer();
	entitiesNotProcessedManager->updateBlock(buffer, blockNumber);
}

bool BookManager::hasNextBook(unsigned int offset){

	if (booksManager->isEndOfFile(offset)) {
		return false;
	} else {
		return true;
	}
}

Book* BookManager::getNextBook(unsigned int offset){

	if(hasNextBook(offset)){
		return getBookByOffset(offset);
	}else{
		return NULL;
	}
}

Book* BookManager::getBookByOffset(int offset){

	Book* book = new Book();
	booksManager->readRecord(book,offset);
	return book;
}

unsigned int BookManager::addBookInCopy(Book* bookToInsert){
	Configuration* config = Configuration::getInstance();
	string path = config->getBookFilePath() + "tempBookFile.dat";
	VarFileManager* newFileBook = new VarFileManager(path);
	unsigned int positionInNewFile = newFileBook->writeRecord(bookToInsert);

	return positionInNewFile;
}

VarFileManager* BookManager::getBooksManager(){
	return booksManager;
}

void BookManager::updateBook(Book* bookToModify){
	bookToModify->setStatus(-1);
	this->booksManager->updateRecord(bookToModify,bookToModify->getEntityID());
}

BookManager::~BookManager() {
	delete booksManager;
	delete entitiesNotProcessedManager;
}
